/**
 * Copyright (C) 2011 The Android Open Source Project 
 * Emial : guoyil199@163.com
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License. 
 */ 
package com.example.androidtest;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Map;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;


/**
 * @author Administrator
 * @Email guoyil199@163.com
 * @version V1.0
 * @Date 2015年3月13日 下午3:36:47
 * @Title <pre>todo</pre>
 * @Description <pre>todo</pre>
 */

public abstract class BaseDao {

	public final static Object lock=new Object();
	private String tbName;
	private final DatabaseHelper dbHelper;
	protected Context context;
	protected SQLiteDatabase wdb,rdb;

	/**
	 * 返回操作表名TBL_NAME，该方法需实现
	 */
	public abstract String getTblName();
	
	public abstract DatabaseHelper getDbHelper(Context context);

	/**
	 * 返回创建表的sql语句
	 */
	public abstract String getCreateTableSql();
	
	public BaseDao(Context context){
		this(context, null);
	}
	
	public BaseDao(Context context,String tblName){
		this.context=context;
		dbHelper=getDbHelper(context);
		if(tblName==null) tblName=getTblName();
		this.tbName=tblName;
		wdb=dbHelper.getWritableDatabase();
		rdb=dbHelper.getReadableDatabase();
		createTable();
	}
	
	/**
	 * 创建表
	 */
	public void createTable(){
		if (wdb!=null) {
			if (!wdb.isOpen()) {
				wdb=dbHelper.getWritableDatabase();
			}
			String sql=getCreateTableSql();
			synchronized(lock){
				wdb.execSQL(sql);
			}
		}
	}
	
	/**
	 * 插入数据
	 * @param values 待插入的数据键值对
	 * @return 对于有自动生成的主键才返回插入的id值
	 * @
	 */
	public long insertBackId(ContentValues values) {
		synchronized(lock){
			SQLiteDatabase db=dbHelper.getWritableDatabase();
			try {
				long id=db.insert(tbName, null, values);
				return id;
			} catch (Exception e) {
				Log.e("BaseDao","insert DaoEception",e);
			}
		}
		return 0;
	}
	
	/**
	 * 插入数据
	 * @param values 待插入的数据键值对
	 * @return 成功返回True,失败返回flase
	 * @
	 */
	public boolean insert(ContentValues values) {
		synchronized(lock){
			SQLiteDatabase db=dbHelper.getWritableDatabase();
			try {
				db.insert(tbName, null, values);
				return true;
			} catch (Exception e) {
				Log.e("BaseDao","insert DaoEception",e);
			}
		}
		return false;
	}
	
	/**
	 * 删除数据
	 * @param whereClause where后面的条件语句
	 * @param 用于填充whereClause的参数
	 * @return 成功返回True,失败返回flase
	 * @
	 */
	public boolean delete(String whereClause,String[] selectionArgs) {
		synchronized(lock){
			SQLiteDatabase db=dbHelper.getWritableDatabase();
			try {
				int res=db.delete(tbName, whereClause, selectionArgs);
				if(res>0){
					return true;
				}
			} catch (Exception e) {
				Log.e("BaseDao","delete DaoEception",e);
			}
		}
		return false;
	}
	
	/**
	 * 修改数据
	 * @param values 待修改的数据键值对
	 * @param whereClause where后面的条件语句
	 * @param 用于填充whereClause的参数
	 * @return 成功返回True,失败返回flase
	 * @
	 */
	public boolean update(ContentValues values,String whereClause,String[] selectionArgs){
		synchronized(lock){
			SQLiteDatabase db=dbHelper.getWritableDatabase();
			try {
				int res = db.update(tbName, values, whereClause, selectionArgs);
				if (res > 0) {
					return true;
				}
			} catch (Exception e) {
				Log.e("BaseDao","update DaoEception",e);
			}
		}
		return false;
	}
	
	/**
	 * 查询单行数据数据
	 * @param clazz 待返回的对象类型,该类型必须具有无参的构造方法
	 * @param whereClause 查询条件即where之后的语句，不需需包含where关键字，可空
	 * @param selectionArgs 用于填充whereClause的参数
	 * @param cols 查询返回的列,如果为null则取所有列
	 * @param paramMap 返回的列名与clazz对象中的属性的映射关系，为null则直接使用cols为属性名，可空
	 * @param groupBy 分组方式，不需包含groupBy关键字，可空
	 * @param orderBy 排序方式，不需包含orderBy关键字，可空
	 * @param limit 筛选返回记录数，需包含limit关键字，可空
	 * @return 没有查询到结果则返回null
	 * @throws InstantiationException 
	 * @throws IllegalAccessException 
	 * @ 
	 */
	@SuppressWarnings("unchecked")
	public <T> T queryForObject(Class<T> clazz,String whereClause,String[] selectionArgs,String[] cols,Map<String, String> paramMap) 
			{
		Cursor c=null;
		T result=null;
		SQLiteDatabase db=dbHelper.getReadableDatabase();
		synchronized (lock) {
			try{
				c=db.query(true, tbName, cols, whereClause, selectionArgs, null, null, null, null);
				if(c.getCount()>0){
					c.moveToFirst();
					if(clazz.isPrimitive() || isNumber(clazz)||isString(clazz)){
						result=(T)getPrimitiveValue(c, clazz);
					}else{
						result=clazz.newInstance();
						int colCount=c.getColumnCount();
						for(int i=0;i<colCount;i++){
							String colName=c.getColumnName(i);
							String key=colName;
							if(paramMap!=null){
								key=paramMap.get(colName);
								if (key==null) {
									key=colName;
								}
							}
							setFieldValue(c, i, key, result);
						}
					}
				}
			}catch (Exception e) {
				Log.e("BaseDao","queryForObject DaoEception",e);
			}finally{
				if(c!=null){
					c.close();
					c=null;
				}
			}
		}
		return result;
	}
	
	/**
	 * 查询单行数据数据
	 * @param clazz 待返回的对象类型,该类型必须具有无参的构造方法
	 * @param tblName 表名
	 * @param whereClause 查询条件即where之后的语句，不需需包含where关键字，可空
	 * @param selectionArgs 用于填充whereClause的参数
	 * @param cols 查询返回的列,如果为null则取所有列
	 * @param paramMap 返回的列名与clazz对象中的属性的映射关系，为null则直接使用cols为属性名，可空
	 * @param groupBy 分组方式，不需包含groupBy关键字，可空
	 * @param orderBy 排序方式，不需包含orderBy关键字，可空
	 * @param limit 筛选返回记录数，需包含limit关键字，可空
	 * @return 没有查询到结果则返回null
	 * @throws InstantiationException 
	 * @throws IllegalAccessException 
	 * @ 
	 */
	@SuppressWarnings("unchecked")
	public <T> T queryForObject(Class<T> clazz,String tblName,String whereClause,String[] selectionArgs,String[] cols,Map<String, String> paramMap) 
			{
		Cursor c=null;
		T result=null;
		SQLiteDatabase db=dbHelper.getReadableDatabase();
		synchronized (lock) {
			try{
				c=db.query(true, tblName, cols, whereClause, selectionArgs, null, null, null, null);
				if(c.getCount()>0){
					c.moveToFirst();
					if(clazz.isPrimitive() || isNumber(clazz)||isString(clazz)){
						result=(T)getPrimitiveValue(c, clazz);
					}else{
						result=clazz.newInstance();
						int colCount=c.getColumnCount();
						for(int i=0;i<colCount;i++){
							String colName=c.getColumnName(i);
							String key=colName;
							if(paramMap!=null){
								key=paramMap.get(colName);
								if (key==null) {
									key=colName;
								}
							}
							setFieldValue(c, i, key, result);
						}
					}
				}
			}catch (Exception e) {
				Log.e("BaseDao","queryForObject DaoEception",e);
			}finally{
				if(c!=null){
					c.close();
					c=null;
				}
			}
		}
		return result;
	}
	
	/**
	 * 查询数据，返回数据列表
	 * @param clazz 待返回的对象类型,该类型必须具有无参的构造方法
	 * @param whereClause 查询条件即where之后的语句，不需需包含where关键字，可空
	 * @param selectionArgs 用于填充whereClause的参数
	 * @param cols 查询返回的列,如果为null则取所有列
	 * @param paramMap 返回的列名与clazz对象中的属性的映射关系，为null则直接使用cols为属性名，可空
	 * @param orderBy 排序方式，不需包含orderBy关键字，可空
	 * @return 没有查询到结果则返回空的list对象
	 * @
	 * @throws InstantiationException 
	 * @throws IllegalAccessException 
	 */
	@SuppressWarnings("unchecked")
	public <T> ArrayList<T> queryForList(Class<T> clazz,String whereClause,String[] selectionArgs,String[] cols
			,Map<String,String> paramMap,String orderBy)  {
		ArrayList<T> res = new ArrayList<T>();
		Cursor c=null;
		SQLiteDatabase db=dbHelper.getReadableDatabase();
		synchronized (lock) {
			try{
				c=db.query(true, tbName, cols, whereClause, selectionArgs, null, null, orderBy, null);
				if(c.getCount()>0){
					c.moveToFirst();
					if(clazz.isPrimitive() || isNumber(clazz)||isString(clazz)){
						do{
							T obj=(T)getPrimitiveValue(c, clazz);
							if(obj!=null){
								res.add(obj);
							}
						}while(c.moveToNext());
					}else{
						do{
							T obj=clazz.newInstance();
							int colCount=c.getColumnCount();
							for(int columnIndex=0;columnIndex<colCount;columnIndex++){
								String colName=c.getColumnName(columnIndex);
								String key=colName;
								if(paramMap!=null){
									key=paramMap.get(colName);
									if (key==null) {
										key=colName;
									}
								}
								setFieldValue(c, columnIndex, key, obj);
							}
							res.add(obj);
						}while(c.moveToNext());
					}
				}
			}catch (Exception e) {
				Log.e("BaseDao","queryForList DaoException",e);
			}finally{
				if(c!=null){
					c.close();
					c=null;
				}
			}
		}
		return res;
	}
	
	/**
	 * 查询数据，返回数据列表
	 * @param clazz 待返回的对象类型,该类型必须具有无参的构造方法
	 * @param tblName 待查询表名
	 * @param whereClause 查询条件即where之后的语句，不需需包含where关键字，可空
	 * @param selectionArgs 用于填充whereClause的参数
	 * @param cols 查询返回的列,如果为null则取所有列
	 * @param paramMap 返回的列名与clazz对象中的属性的映射关系，为null则直接使用cols为属性名，可空
	 * @param orderBy 排序方式，不需包含orderBy关键字，可空
	 * @return 没有查询到结果则返回空的list对象
	 * @
	 * @throws InstantiationException 
	 * @throws IllegalAccessException 
	 */
	@SuppressWarnings("unchecked")
	public <T> ArrayList<T> queryForList(Class<T> clazz,String tblName,String whereClause,String[] selectionArgs,
			String[] cols,Map<String,String> paramMap,String orderBy)  {
		ArrayList<T> res = new ArrayList<T>();
		Cursor c=null;
		SQLiteDatabase db=dbHelper.getReadableDatabase();
		synchronized (lock) {
			try{
				c=db.query(true, tblName, cols, whereClause, selectionArgs, null, null, orderBy, null);
				if(c.getCount()>0){
					c.moveToFirst();
					if(clazz.isPrimitive() || isNumber(clazz)||isString(clazz)){
						do{
							T obj=(T)getPrimitiveValue(c, clazz);
							if(obj!=null){
								res.add(obj);
							}
						}while(c.moveToNext());
					}else{
						do{
							T obj=clazz.newInstance();
							int colCount=c.getColumnCount();
							for(int columnIndex=0;columnIndex<colCount;columnIndex++){
								String colName=c.getColumnName(columnIndex);
								String key=colName;
								if(paramMap!=null){
									key=paramMap.get(colName);
									if (key==null) {
										key=colName;
									}
								}
								setFieldValue(c, columnIndex, key, obj);
							}
							res.add(obj);
						}while(c.moveToNext());
					}
				}
			}catch (Exception e) {
				Log.e("BaseDao","queryForList DaoException",e);
			}finally{
				if(c!=null){
					c.close();
					c=null;
				}
			}
		}
		return res;
	}
	
	/**
	 * 查询数据，返回数据列表
	 * @param clazz 待返回的对象类型,该类型必须具有无参的构造方法
	 * @param whereClause 查询条件即where之后的语句，不需需包含where关键字，可空
	 * @param cols 查询返回的列,如果为null则取所有列
	 * @param paramMap 返回的列名与clazz对象中的属性的映射关系，为null则直接使用cols为属性名，可空
	 * @param groupBy 分组方式，不需包含groupBy关键字，可空
	 * @param orderBy 排序方式，不需包含orderBy关键字，可空
	 * @param firstIdx 起始索引
	 * @param maxResult 最大返回记录数
	 * @return 没有查询到结果则返回空的list对象
	 * @
	 * @throws InstantiationException 
	 * @throws IllegalAccessException 
	 */
	@SuppressWarnings("unchecked")
	public <T> ArrayList<T> queryForList(Class<T> clazz,String whereClause,String[] selectionArgs,String[] cols,
			Map<String, String> paramMap,String groupBy,String orderBy, int firstIdx,int maxResult) {
		ArrayList<T> res = new ArrayList<T>();
		Cursor c=null;
		String limit=null;
		if(firstIdx>=0 && maxResult>0){
			limit=firstIdx+","+maxResult;
		}
		SQLiteDatabase db=dbHelper.getReadableDatabase();
		synchronized (lock) {
			try{
				c=db.query(true, tbName, cols, whereClause, selectionArgs, groupBy, null, orderBy, limit);
				if(c.getCount()>0){
					c.moveToFirst();
					if(clazz.isPrimitive() || isNumber(clazz)||isString(clazz)){
						do{
							T obj=(T)getPrimitiveValue(c, clazz);
							if(obj!=null){
								res.add(obj);
							}
						}while(c.moveToNext());
					}else{
						do{
							T obj=clazz.newInstance();
							int colCount=c.getColumnCount();
							for(int columnIndex=0;columnIndex<colCount;columnIndex++){
								String colName=c.getColumnName(columnIndex);
								String key=colName;
								if(paramMap!=null){
									key=paramMap.get(colName);
									if (key==null) {
										key=colName;
									}
								}
								setFieldValue(c, columnIndex, key, obj);
							}
							res.add(obj);
						}while(c.moveToNext());
					}
				}
			}catch (Exception e) {
				Log.e("BaseDao","queryForList DaoException",e);
			}finally{
				if(c!=null){
					c.close();
					c=null;
				}
			}	
		}
		
		return res;
	}
	
	public long queryCount(String whereClause,String[] selectionArgs) {
		SQLiteDatabase db=dbHelper.getReadableDatabase();
		Cursor c=null;
		long count=0;
		synchronized (lock) {
			try {
				c=db.query(tbName, new String[]{"count(*)"}, whereClause, selectionArgs, null, null, null);
				if(c.getCount()>0){
					c.moveToFirst();
					count=c.getLong(0);
				}
			} catch (Exception e) {
				Log.e("BaseDao","queryCount DaoException",e);
			}finally{
				if(c!=null){
					c.close();
					c=null;
				}
			}
		}
		return count;
	}
	@SuppressWarnings("unchecked")
	private <T> T getPrimitiveValue(Cursor c,Class<?> clazz){
		int columnIndex=0;
		if (Short.TYPE.isAssignableFrom(clazz)) {
			Short value=c.getShort(columnIndex);
			return (T)value;
		} else if(Integer.TYPE.isAssignableFrom(clazz)) {
			Integer value=c.getInt(columnIndex);
			return (T)value;
		} else if (Long.TYPE.isAssignableFrom(clazz)) {
			Long value=c.getLong(columnIndex);
			return (T)value;
		} else if(Float.TYPE.isAssignableFrom(clazz)){
			Float value=c.getFloat(columnIndex);
			return (T)value;
		} else if(Double.TYPE.isAssignableFrom(clazz)){
			Double value=c.getDouble(columnIndex);
			return (T)value;
		}else if(Boolean.TYPE.isAssignableFrom(clazz)) { // 二进制数据
			byte value[]=c.getBlob(columnIndex);
			return (T)value;
		}else if(String.class.isAssignableFrom(clazz)){
			String value=c.getString(columnIndex);
			return (T)value;
		}else{
			Log.e("BaseDao getPrimitiveValue","该数据类型不是基本的数据类型！");
			return (T)null;
		}
	}
	
	public static boolean isNumber(Class<?> clazz) {
		return clazz != null && (Short.TYPE.isAssignableFrom(clazz)
				|| Integer.TYPE.isAssignableFrom(clazz)
				|| Long.TYPE.isAssignableFrom(clazz)
				|| Float.TYPE.isAssignableFrom(clazz)
				|| Double.TYPE.isAssignableFrom(clazz) 
				|| Number.class.isAssignableFrom(clazz));
	}
	
	public static boolean isString(Class<?> clazz) {
		return clazz != null && (String.class.isAssignableFrom(clazz) 
				|| (Character.TYPE.isAssignableFrom(clazz) 
				|| Character.class.isAssignableFrom(clazz)));
	}
	
	/**
	 * 使用反射机制映射各列的值到目标对象
	 * @param <T>
	 * @param c 游标
	 * @param columnIndex 当前取值的列索引
	 * @param key 待注入到T类型中的属性名
	 * @param obj 待设置值的对象
	 * @return 成功返回True,失败或异常返回false
	 */
	private <T> boolean setFieldValue(Cursor c,int columnIndex,String key,T obj) {
		try {
			Field field = obj.getClass().getDeclaredField(key);
			if (field == null) {
				return false;
			}
			field.setAccessible(true);
			Class<?> fieldType = field.getType();
			if (Short.TYPE.isAssignableFrom(fieldType)) {
				short value=c.getShort(columnIndex);
				field.set(obj, value);
			} else if(Integer.TYPE.isAssignableFrom(fieldType)) { // 数组、集合、map暂不处理
				int value=c.getInt(columnIndex);
				field.set(obj, value);
			} else if (Long.TYPE.isAssignableFrom(fieldType)) {
				long value=c.getLong(columnIndex);
				field.set(obj, value);
			} else if(Float.TYPE.isAssignableFrom(fieldType)){
				float value=c.getFloat(columnIndex);
				field.set(obj, value);
			} else if(Double.TYPE.isAssignableFrom(fieldType)){
				double value=c.getDouble(columnIndex);
				field.set(obj, value);
			}else if(Boolean.TYPE.isAssignableFrom(fieldType)) { // 二进制数据
				byte value[]=c.getBlob(columnIndex);
				field.set(obj, value);
			}else if(String.class.isAssignableFrom(fieldType)){
				String value=c.getString(columnIndex);
				field.set(obj, value);
			}else{
				Log.e("BaseDao setFieldValue","属性："+key+"的数据类型为数据库中不支持的类型！");
				return false;
			}
			return true;
		} catch (SecurityException e) {
			Log.e("BaseDao","setFieldValue SecurityException", e);
		} catch (NoSuchFieldException e) {
			Log.e("BaseDao","setFieldValue NoSuchFieldException",e);
		}catch (IllegalArgumentException e) {
			Log.e("BaseDao","setFieldValue IllegalArgumentException" ,e);
		} catch (IllegalAccessException e) {
			Log.e("BaseDao","setFieldValue IllegalAccessException", e);
		}
		return false;
	}
	
	public int deleteAll(String tbName){
		SQLiteDatabase db=dbHelper.getWritableDatabase();
		try{
			db.execSQL("delete from "+tbName);
			db.execSQL("select * from sqlite_sequence");
			db.execSQL("update sqlite_sequence set seq=0 where name='"+tbName+"'");
			return 1;
		}catch (Exception e) {
			Log.e("BaseDao",e.getMessage(),e);
		}
		return 0;
	}
	
	public void close() {
		try {
			wdb.close();
			rdb.close();
			wdb=null;
			rdb=null;
		} catch (Exception e) {
			Log.e("BaseDao",e.getMessage(),e);
		}
	}
	
	public void removeTable(String tbName){
		synchronized(lock){
			if(dbHelper.tabbleIsExist(wdb,tbName)){
				String tblsql=" delete from "+ tbName;
				wdb.execSQL(tblsql);
			}
			/*SQLiteDatabase db=wdb;
			dbHelper.clearData(db);*/
		}
	}
	
	/**
	 * @Description 开始事务
	 * @return void
	 */
	public void beginTransaction(){
		dbHelper.getWritableDatabase().beginTransaction();
	}
	/**
	 * @Description 设置事务成功完成 
	 * @return void
	 */
	public void setTransactionSuccessful(){
		dbHelper.getWritableDatabase().setTransactionSuccessful();
	}
	/**
	 * @Description 结束事务 
	 * @return void
	 */
	public void endTransaction(){
		dbHelper.getWritableDatabase().endTransaction();
	}
	
}
